knitr::opts_knit$set(root.dir = "/Users/amitmeir/Documents/rglab/flowReMix")

Loading Data

library(flowReMix)
library(ggplot2)
library(dplyr)
library(reshape2)
library(cowplot)
assign <- function(x) {
  x$prop <- x$count / x$parentcount
  assign <- as.numeric(by(x, x$subset, function(y) y$prop[1] > y$prop[2]))
  assign[assign == 1] <- -1
  result <- data.frame(ptid = x$ptid[1], subset = unique(x$subset), assign = assign)
  return(result)
}
require(pROC)
require(reshape2)
data("rv144_booleans")
bySubset <- by(data.frame(booleans$stim, booleans$nonstim), booleans$Subset, function(x) x)
largerThanThershold <- sapply(bySubset, function(x) colSums(x >5))
booldata <- melt(booleans, c("PTID", "Subset"))
names(booldata)[3:4] <- c("stim", "count")
booldata <- by(booldata, INDICES = list(booldata$PTID, booldata$stim), function(x) {
  x$parentcount <- sum(x$count)
  return(x)
})
booldata <- do.call("rbind", booldata)
booldata <- subset(booldata, Subset != "!TNFa&!IFNg&!IL4&!IL2&!CD154&!IL17a")
booldata$treatment <- as.numeric(booldata$stim == "stim")
uniquepop <- unique(booldata$Subset)
booldata <- with(booldata, booldata[order(Subset, PTID, stim, decreasing = FALSE), ])
booldata <- subset(booldata, !is.na(Subset))
allsubset <- booldata
booldata <- with(booldata, booldata[order(Subset, PTID, stim, decreasing = FALSE), ])
# Naming ------------------
subsets <- unique(booldata$Subset)
booldata$Subset <- as.character(booldata$Subset)
nfunctions <- numeric(length(subsets))
for(i in 1:length(subsets)) {
  split <- strsplit(as.character(subsets[i]), "&")[[1]]
  first <- substr(split, 1, 1)
  nfunction <- sum(first != "!")
  nfunctions[i] <- nfunction
  name <- paste(split[first != "!"], collapse = ",")
  booldata$nfunction[booldata$Subset == subsets[[i]]] <- nfunction
  booldata$Subset[booldata$Subset == subsets[[i]]] <- name
}
subsets <- unique(booldata$Subset)
booldata <- with(booldata, booldata[order(Subset, PTID, stim, decreasing = FALSE), ])
names(booldata) <- tolower(names(booldata))
# Getting vaccine information --------------------
data("rv144")
rv144 <- rv144[order(rv144$ptid), ]
vaccine <- (by(rv144, rv144$ptid, function(x) x$vaccine[1] == "VACCINE"))
vaccine <- data.frame(ptid = names(vaccine), vaccine = as.numeric(vaccine))
vaccinemat <- vaccine[vaccine$ptid %in% booldata$ptid, ]
# Getting infection status
data("rv144_correlates_data")
correlates <- rv144_correlates_data
correlates <- correlates[order(as.character(correlates$PTID)), ]
infection <- correlates$infect.y
# Converting low counts to booleans --------------
countByPop <- by(booldata, booldata$subset, function(x) max(x$count / x$parentcount) < 10^-3 / 2)
countByPop <- by(booldata, booldata$subset, function(x) {
  if(max(x$count / x$parentcount) < 10^-3 / 2) {
    x$count <- as.numeric(x$count > 0)
    x$parentcount <- 1
  }
  return(x)
})
# booldata <- do.call("rbind", countByPop)

Data Analysis

library(flowReMix)
control <- flowReMix_control(updateLag = 25, nsamp = 100, initMHcoef = 2.5,
                             nPosteriors = 1, centerCovariance = TRUE,
                             maxDispersion = 10^3, minDispersion = 10^7,
                             randomAssignProb = 10^-8, intSampSize = 50,
                             lastSample = 100, isingInit = -log(99),
                             initMethod = "robust")

booldata$subset <- factor(booldata$subset)
preAssignment <- do.call("rbind", by(booldata, booldata$ptid, assign))
system.time(fit <- flowReMix(cbind(count, parentcount - count) ~ treatment,
                 subject_id = ptid,
                 cell_type = subset,
                 cluster_variable = treatment,
                 data = booldata,
                 covariance = "sparse",
                 ising_model = "sparse",
                 regression_method = "robust",
                 iterations = 40,
                 # cluster_assignment = preAssignment,
                 parallel = TRUE,
                 verbose = TRUE, control = control))

Loading Results

load(file = "data analysis/results/boolean robust15.Robj")

Scatter Plots

ids <- fit$posteriors[, 1:2]
vaccine[, 1] <- as.character(vaccine[, 1])
vaccine[, 1] <- factor(vaccine[, 1], levels = levels(ids[, 1]))
vaccine <- vaccine[!is.na(vaccine[, 1]), ]
vaccine <- vaccine[order(vaccine[, 1]), ]
ids <- merge(ids, vaccine, all.x = TRUE, all.y = FALSE,
                 by = "ptid", sort = FALSE)
vaccination <- ids[, 3]
scatter <- plot(fit, target = vaccination, type = "scatter",
                ncol = 4)
scatter + theme_bw()

ROC plot

rocplot <- plot(fit, target = vaccination, type = "ROC", ncols = 5,
     direction = "auto", thresholdPalette = NULL,
     subsets = NULL)
rocplot

FDR Curves

plot(fit, target = vaccination, type = "FDR")

ROC table

rocResults <- rocTable(fit, vaccination, direction = ">", adjust = "BH",
                       sortAUC = FALSE)
rocResults[order(rocResults$auc, decreasing = TRUE), ]

Some Functionality Analysis

# Getting infection status
infectDat <- data.frame(ptid = rv144_correlates_data$PTID, infect = rv144_correlates_data$infect.y)
datId <- as.character(fit$posteriors$ptid)
infectID <- as.character(infectDat$ptid)
infectDat <- infectDat[infectID %in% datId, ]
infectDat$ptid <- factor(as.character(infectDat$ptid), levels = levels(booldata$ptid))
infectDat <- infectDat[order(infectDat$ptid), ]
ids <- merge(ids, infectDat, sort = FALSE)
infect <- ids[, 4]
infect[infect == "PLACEBO"] <- NA
# Computing Functionality Score
func <- rowSums(fit$posteriors[, -1])
funcAUC <- roc(infect ~ func)$auc
n0 <- sum(infect == "INFECTED", na.rm = TRUE)
n1 <- sum(infect == "NON-INFECTED", na.rm = TRUE)
print("AUC for detecting infection based on functionality score.")
[1] "AUC for detecting infection based on functionality score."
funcAUC
Area under the curve: 0.6126
pwilcox(funcAUC * n0 * n1, n0, n1, lower.tail = FALSE)
[1] 0.01412133
# Computing polyfunctionality score
nfunctions <- sapply(subsets, function(x) length(gregexpr(",", paste(",", x))[[1]]))
M <- 6
weights <- nfunctions / (choose(M, nfunctions))
poly <- apply(fit$posteriors[, -1], 1, function(x) weighted.mean(x, weights))
# poly <- apply(fit$posteriors[, -1], 1, function(x) median(weights * x))
polyAUC <- roc(infect ~ poly)$auc
n0 <- sum(infect == "INFECTED", na.rm = TRUE)
n1 <- sum(infect == "NON-INFECTED", na.rm = TRUE)
print("AUC for detecting infection based on polyfunctionality score.")
[1] "AUC for detecting infection based on polyfunctionality score."
polyAUC
Area under the curve: 0.6221
pwilcox(polyAUC * n0 * n1, n0, n1, lower.tail = FALSE)
[1] 0.008605355
print("AUCs for dicriminating between Vaccinees and Placebos based on functionality and polyfunctionality scores.")
[1] "AUCs for dicriminating between Vaccinees and Placebos based on functionality and polyfunctionality scores."
roc(vaccination ~ func)

Call:
roc.formula(formula = vaccination ~ func)

Data: func in 36 controls (vaccination 0) < 226 cases (vaccination 1).
Area under the curve: 0.9929
roc(vaccination ~ poly)

Call:
roc.formula(formula = vaccination ~ poly)

Data: poly in 36 controls (vaccination 0) < 226 cases (vaccination 1).
Area under the curve: 0.9931

Polyfunctionality Boxplots

hiv <- infect
nfunctions <- sapply(names(fit$posteriors)[-1], function(x) length(strsplit(x, ",", fixed = TRUE)[[1]]))
weights <- list()
weights$FS <- rep(1, ncol(fit$posteriors) - 1)
M <- 6
weights$PFS <- nfunctions / choose(6, nfunctions)
hiv[is.na(hiv)] <- "PLACEBO"
plot(fit, target = hiv, type = "boxplot", groups = "all", weights = weights)

Logistic Regressions

group <- c(24, 21, 15, 8)
score <- rowMeans(fit$posteriors[, group])
rocfit <- roc(infect ~ score)
pwilcox(rocfit$auc * n0 * n1, n0, n1, lower.tail = FALSE)
[1] 0.005360221
ids$groupscore <- score
ids$poly <- poly
ids$func <- func
vaccines <- subset(correlates, infect.y != "PLACEBO")
vaccines$PTID <- as.character(vaccines$PTID)
ids$ptid <- as.character(ids$ptid)
vaccines <- merge(vaccines, ids, all.x = TRUE, all.y = TRUE,
                  by.x = "PTID", by.y = "ptid")
vaccines <- subset(vaccines, vaccines$infect != "PLACEBO")
plot(vaccines$PFS, vaccines$poly, main = "COMPASS polyfunctionality vs. new polyfunctionality")
lines(lowess(vaccines$PFS, vaccines$poly), col = "red", lwd = 2)
abline(v = c(0.05, .085))
abline(h = c(0.35))

target <- vaccines$poly > 0.35 & vaccines$PFS > 0.05 & vaccines$PFS < 0.085
summary(glm(infect ~ func + IgAprim + risk.medium + risk.high + sex,
            family = "binomial",
            data = vaccines))

Call:
glm(formula = infect ~ func + IgAprim + risk.medium + risk.high + 
    sex, family = "binomial", data = vaccines)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.2258  -0.6466  -0.5075  -0.3969   2.4050  

Coefficients:
              Estimate Std. Error z value Pr(>|z|)   
(Intercept) -6.087e-03  1.024e+00  -0.006  0.99526   
func        -1.702e-01  9.385e-02  -1.813  0.06979 . 
IgAprim      4.862e-01  1.818e-01   2.674  0.00749 **
risk.medium -6.699e-05  4.708e-01   0.000  0.99989   
risk.high    6.324e-01  4.266e-01   1.482  0.13827   
sex         -4.352e-02  3.863e-01  -0.113  0.91031   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 204.72  on 225  degrees of freedom
Residual deviance: 192.57  on 220  degrees of freedom
AIC: 204.57

Number of Fisher Scoring iterations: 4
summary(glm(infect ~ poly + IgAprim + risk.medium + risk.high + sex,
            family = "binomial",
            data = vaccines))

Call:
glm(formula = infect ~ poly + IgAprim + risk.medium + risk.high + 
    sex, family = "binomial", data = vaccines)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.2498  -0.6299  -0.5132  -0.3850   2.4341  

Coefficients:
             Estimate Std. Error z value Pr(>|z|)   
(Intercept)  0.381070   1.101848   0.346  0.72946   
poly        -4.224542   2.079878  -2.031  0.04224 * 
IgAprim      0.477983   0.180938   2.642  0.00825 **
risk.medium -0.001307   0.471387  -0.003  0.99779   
risk.high    0.627893   0.427398   1.469  0.14180   
sex         -0.044041   0.387241  -0.114  0.90945   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 204.72  on 225  degrees of freedom
Residual deviance: 191.68  on 220  degrees of freedom
AIC: 203.68

Number of Fisher Scoring iterations: 4
summary(glm(infect ~ groupscore + IgAprim + risk.medium + risk.high + sex,
            family = "binomial",
            data = vaccines))

Call:
glm(formula = infect ~ groupscore + IgAprim + risk.medium + risk.high + 
    sex, family = "binomial", data = vaccines)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-1.2799  -0.6283  -0.5071  -0.3711   2.5203  

Coefficients:
            Estimate Std. Error z value Pr(>|z|)   
(Intercept) -0.30977    0.70412  -0.440  0.65998   
groupscore  -2.45746    1.06876  -2.299  0.02148 * 
IgAprim      0.47807    0.18117   2.639  0.00832 **
risk.medium  0.01706    0.47256   0.036  0.97120   
risk.high    0.61467    0.42860   1.434  0.15153   
sex         -0.04239    0.38871  -0.109  0.91316   
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 204.72  on 225  degrees of freedom
Residual deviance: 190.15  on 220  degrees of freedom
AIC: 202.15

Number of Fisher Scoring iterations: 5

Logistic Regressions for Single Subsets

vaccines <- subset(correlates, infect.y != "PLACEBO")
resultList <- list()
adjRocList <- list()
for(i in 1:(ncol(fit$posteriors) - 1)) {
  vaccines$post <- NULL
  post <- fit$posteriors[!is.na(infect), c(1, i + 1)]
  names(post)[2] <- "post"
  vaccines <- merge(vaccines, post, by.x = "PTID", by.y = "ptid", all.x = TRUE)
  resultList[[i]] <- summary(glm(infect.y ~ post + IgAprim + agecat + risk.medium + risk.high + sex,
                                 family = "binomial",
                                 data = vaccines))
  resid <- lm(post ~ IgAprim + agecat + risk.medium + risk.high + sex,
              data = vaccines)$residuals
  infectResid <- glm(infect.y  ~ IgAprim + agecat + risk.medium + risk.high + sex,
                     family = "binomial", data = vaccines)$residuals
  adjRocList[[i]] <- roc(vaccines$infect.y ~ resid)
}
names(resultList) <- colnames(fit$posteriors)[-1]
names(adjRocList) <- colnames(fit$posteriors)[-1]
regResult <- t(sapply(resultList, function(x) x$coefficient[2, c(1,4)]))
regResult <- data.frame(regResult)
regResult$auc <- sapply(adjRocList, function(x) x$auc)
regResult$aucPval <- pwilcox(regResult$auc * n0 * n1, n0, n1, lower.tail = FALSE)
regResult$aucQval <- p.adjust(regResult$aucPval, method = "BH")
regResult[order(regResult[, 2], decreasing = FALSE), ]

Dependence Structure for Random Effects for Varying Threshold

# randStability <- stabilityGraph(fit, type = "randomEffects", cpus = 2, reps = 100,
#                                 cv = TRUE)
# save(randStability, file = "data analysis/results/RV144rand15.Robj")
load("data analysis/results/RV144rand15.Robj")
for(threshold in c(0.5, 0.75, 0.85, 0.9, 0.95, 1)) {
  randplot <- plot(randStability, fill = rocResults$auc, threshold = threshold)
  print(randplot)
}
Loading required package: network
network: Classes for Relational Data
Version 1.13.0 created on 2015-08-31.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
                    Mark S. Handcock, University of California -- Los Angeles
                    David R. Hunter, Penn State University
                    Martina Morris, University of Washington
                    Skye Bender-deMoll, University of Washington
 For citation information, type citation("network").
 Type help("network-package") to get started.

Loading required package: sna
Loading required package: statnet.common
sna: Tools for Social Network Analysis
Version 2.4 created on 2016-07-23.
copyright (c) 2005, Carter T. Butts, University of California-Irvine
 For citation information, type citation("sna").
 Type help(package="sna") to get started.

Loading required package: scales

Ising Network for Varying Threshold

# stability <- stabilityGraph(fit, type = "ising", cpus = 2, reps = 100)
# save(stability, file = "data analysis/results/RV144ising15.Robj")
load("data analysis/results/RV144ising15.Robj")
for(threshold in c(0.5, 0.75, 0.85, 0.9, 0.95, 1)) {
  isingplot <- plot(stability, fill = rocResults$auc, threshold = threshold)
  print(isingplot)
}

LS0tCnRpdGxlOiAiUlYxNDQgQW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KYGBge3IgInNldHVwIiwgaW5jbHVkZT1UUlVFLCB3YXJuaW5nPUZBTFNFfQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICIvVXNlcnMvYW1pdG1laXIvRG9jdW1lbnRzL3JnbGFiL2Zsb3dSZU1peCIpCmBgYAoKKipMb2FkaW5nIERhdGEqKgoKYGBge3IsIHJlc3VsdHMgPSAiaGlkZSIsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZmxvd1JlTWl4KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoY293cGxvdCkKCmFzc2lnbiA8LSBmdW5jdGlvbih4KSB7CiAgeCRwcm9wIDwtIHgkY291bnQgLyB4JHBhcmVudGNvdW50CiAgYXNzaWduIDwtIGFzLm51bWVyaWMoYnkoeCwgeCRzdWJzZXQsIGZ1bmN0aW9uKHkpIHkkcHJvcFsxXSA+IHkkcHJvcFsyXSkpCiAgYXNzaWduW2Fzc2lnbiA9PSAxXSA8LSAtMQogIHJlc3VsdCA8LSBkYXRhLmZyYW1lKHB0aWQgPSB4JHB0aWRbMV0sIHN1YnNldCA9IHVuaXF1ZSh4JHN1YnNldCksIGFzc2lnbiA9IGFzc2lnbikKICByZXR1cm4ocmVzdWx0KQp9CgpyZXF1aXJlKHBST0MpCnJlcXVpcmUocmVzaGFwZTIpCmRhdGEoInJ2MTQ0X2Jvb2xlYW5zIikKYnlTdWJzZXQgPC0gYnkoZGF0YS5mcmFtZShib29sZWFucyRzdGltLCBib29sZWFucyRub25zdGltKSwgYm9vbGVhbnMkU3Vic2V0LCBmdW5jdGlvbih4KSB4KQpsYXJnZXJUaGFuVGhlcnNob2xkIDwtIHNhcHBseShieVN1YnNldCwgZnVuY3Rpb24oeCkgY29sU3Vtcyh4ID41KSkKCmJvb2xkYXRhIDwtIG1lbHQoYm9vbGVhbnMsIGMoIlBUSUQiLCAiU3Vic2V0IikpCm5hbWVzKGJvb2xkYXRhKVszOjRdIDwtIGMoInN0aW0iLCAiY291bnQiKQoKYm9vbGRhdGEgPC0gYnkoYm9vbGRhdGEsIElORElDRVMgPSBsaXN0KGJvb2xkYXRhJFBUSUQsIGJvb2xkYXRhJHN0aW0pLCBmdW5jdGlvbih4KSB7CiAgeCRwYXJlbnRjb3VudCA8LSBzdW0oeCRjb3VudCkKICByZXR1cm4oeCkKfSkKYm9vbGRhdGEgPC0gZG8uY2FsbCgicmJpbmQiLCBib29sZGF0YSkKCmJvb2xkYXRhIDwtIHN1YnNldChib29sZGF0YSwgU3Vic2V0ICE9ICIhVE5GYSYhSUZOZyYhSUw0JiFJTDImIUNEMTU0JiFJTDE3YSIpCmJvb2xkYXRhJHRyZWF0bWVudCA8LSBhcy5udW1lcmljKGJvb2xkYXRhJHN0aW0gPT0gInN0aW0iKQp1bmlxdWVwb3AgPC0gdW5pcXVlKGJvb2xkYXRhJFN1YnNldCkKYm9vbGRhdGEgPC0gd2l0aChib29sZGF0YSwgYm9vbGRhdGFbb3JkZXIoU3Vic2V0LCBQVElELCBzdGltLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdKQpib29sZGF0YSA8LSBzdWJzZXQoYm9vbGRhdGEsICFpcy5uYShTdWJzZXQpKQphbGxzdWJzZXQgPC0gYm9vbGRhdGEKYm9vbGRhdGEgPC0gd2l0aChib29sZGF0YSwgYm9vbGRhdGFbb3JkZXIoU3Vic2V0LCBQVElELCBzdGltLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdKQoKIyBOYW1pbmcgLS0tLS0tLS0tLS0tLS0tLS0tCnN1YnNldHMgPC0gdW5pcXVlKGJvb2xkYXRhJFN1YnNldCkKYm9vbGRhdGEkU3Vic2V0IDwtIGFzLmNoYXJhY3Rlcihib29sZGF0YSRTdWJzZXQpCm5mdW5jdGlvbnMgPC0gbnVtZXJpYyhsZW5ndGgoc3Vic2V0cykpCmZvcihpIGluIDE6bGVuZ3RoKHN1YnNldHMpKSB7CiAgc3BsaXQgPC0gc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKHN1YnNldHNbaV0pLCAiJiIpW1sxXV0KICBmaXJzdCA8LSBzdWJzdHIoc3BsaXQsIDEsIDEpCiAgbmZ1bmN0aW9uIDwtIHN1bShmaXJzdCAhPSAiISIpCiAgbmZ1bmN0aW9uc1tpXSA8LSBuZnVuY3Rpb24KICBuYW1lIDwtIHBhc3RlKHNwbGl0W2ZpcnN0ICE9ICIhIl0sIGNvbGxhcHNlID0gIiwiKQogIGJvb2xkYXRhJG5mdW5jdGlvbltib29sZGF0YSRTdWJzZXQgPT0gc3Vic2V0c1tbaV1dXSA8LSBuZnVuY3Rpb24KICBib29sZGF0YSRTdWJzZXRbYm9vbGRhdGEkU3Vic2V0ID09IHN1YnNldHNbW2ldXV0gPC0gbmFtZQp9CnN1YnNldHMgPC0gdW5pcXVlKGJvb2xkYXRhJFN1YnNldCkKYm9vbGRhdGEgPC0gd2l0aChib29sZGF0YSwgYm9vbGRhdGFbb3JkZXIoU3Vic2V0LCBQVElELCBzdGltLCBkZWNyZWFzaW5nID0gRkFMU0UpLCBdKQpuYW1lcyhib29sZGF0YSkgPC0gdG9sb3dlcihuYW1lcyhib29sZGF0YSkpCgojIEdldHRpbmcgdmFjY2luZSBpbmZvcm1hdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLQpkYXRhKCJydjE0NCIpCnJ2MTQ0IDwtIHJ2MTQ0W29yZGVyKHJ2MTQ0JHB0aWQpLCBdCnZhY2NpbmUgPC0gKGJ5KHJ2MTQ0LCBydjE0NCRwdGlkLCBmdW5jdGlvbih4KSB4JHZhY2NpbmVbMV0gPT0gIlZBQ0NJTkUiKSkKdmFjY2luZSA8LSBkYXRhLmZyYW1lKHB0aWQgPSBuYW1lcyh2YWNjaW5lKSwgdmFjY2luZSA9IGFzLm51bWVyaWModmFjY2luZSkpCnZhY2NpbmVtYXQgPC0gdmFjY2luZVt2YWNjaW5lJHB0aWQgJWluJSBib29sZGF0YSRwdGlkLCBdCgojIEdldHRpbmcgaW5mZWN0aW9uIHN0YXR1cwpkYXRhKCJydjE0NF9jb3JyZWxhdGVzX2RhdGEiKQpjb3JyZWxhdGVzIDwtIHJ2MTQ0X2NvcnJlbGF0ZXNfZGF0YQpjb3JyZWxhdGVzIDwtIGNvcnJlbGF0ZXNbb3JkZXIoYXMuY2hhcmFjdGVyKGNvcnJlbGF0ZXMkUFRJRCkpLCBdCmluZmVjdGlvbiA8LSBjb3JyZWxhdGVzJGluZmVjdC55CgojIENvbnZlcnRpbmcgbG93IGNvdW50cyB0byBib29sZWFucyAtLS0tLS0tLS0tLS0tLQpjb3VudEJ5UG9wIDwtIGJ5KGJvb2xkYXRhLCBib29sZGF0YSRzdWJzZXQsIGZ1bmN0aW9uKHgpIG1heCh4JGNvdW50IC8geCRwYXJlbnRjb3VudCkgPCAxMF4tMyAvIDIpCmNvdW50QnlQb3AgPC0gYnkoYm9vbGRhdGEsIGJvb2xkYXRhJHN1YnNldCwgZnVuY3Rpb24oeCkgewogIGlmKG1heCh4JGNvdW50IC8geCRwYXJlbnRjb3VudCkgPCAxMF4tMyAvIDIpIHsKICAgIHgkY291bnQgPC0gYXMubnVtZXJpYyh4JGNvdW50ID4gMCkKICAgIHgkcGFyZW50Y291bnQgPC0gMQogIH0KICByZXR1cm4oeCkKfSkKIyBib29sZGF0YSA8LSBkby5jYWxsKCJyYmluZCIsIGNvdW50QnlQb3ApCmBgYAoKKipEYXRhIEFuYWx5c2lzKioKYGBge3IsIGV2YWw9RkFMU0V9CmxpYnJhcnkoZmxvd1JlTWl4KQpjb250cm9sIDwtIGZsb3dSZU1peF9jb250cm9sKHVwZGF0ZUxhZyA9IDI1LCBuc2FtcCA9IDEwMCwgaW5pdE1IY29lZiA9IDIuNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuUG9zdGVyaW9ycyA9IDEsIGNlbnRlckNvdmFyaWFuY2UgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heERpc3BlcnNpb24gPSAxMF4zLCBtaW5EaXNwZXJzaW9uID0gMTBeNywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5kb21Bc3NpZ25Qcm9iID0gMTBeLTgsIGludFNhbXBTaXplID0gNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdFNhbXBsZSA9IDEwMCwgaXNpbmdJbml0ID0gLWxvZyg5OSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5pdE1ldGhvZCA9ICJyb2J1c3QiKQoKYm9vbGRhdGEkc3Vic2V0IDwtIGZhY3Rvcihib29sZGF0YSRzdWJzZXQpCnByZUFzc2lnbm1lbnQgPC0gZG8uY2FsbCgicmJpbmQiLCBieShib29sZGF0YSwgYm9vbGRhdGEkcHRpZCwgYXNzaWduKSkKc3lzdGVtLnRpbWUoZml0IDwtIGZsb3dSZU1peChjYmluZChjb3VudCwgcGFyZW50Y291bnQgLSBjb3VudCkgfiB0cmVhdG1lbnQsCiAgICAgICAgICAgICAgICAgc3ViamVjdF9pZCA9IHB0aWQsCiAgICAgICAgICAgICAgICAgY2VsbF90eXBlID0gc3Vic2V0LAogICAgICAgICAgICAgICAgIGNsdXN0ZXJfdmFyaWFibGUgPSB0cmVhdG1lbnQsCiAgICAgICAgICAgICAgICAgZGF0YSA9IGJvb2xkYXRhLAogICAgICAgICAgICAgICAgIGNvdmFyaWFuY2UgPSAic3BhcnNlIiwKICAgICAgICAgICAgICAgICBpc2luZ19tb2RlbCA9ICJzcGFyc2UiLAogICAgICAgICAgICAgICAgIHJlZ3Jlc3Npb25fbWV0aG9kID0gInJvYnVzdCIsCiAgICAgICAgICAgICAgICAgaXRlcmF0aW9ucyA9IDQwLAogICAgICAgICAgICAgICAgICMgY2x1c3Rlcl9hc3NpZ25tZW50ID0gcHJlQXNzaWdubWVudCwKICAgICAgICAgICAgICAgICBwYXJhbGxlbCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUsIGNvbnRyb2wgPSBjb250cm9sKSkKYGBgCgoqKkxvYWRpbmcgUmVzdWx0cyoqCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpsb2FkKGZpbGUgPSAiZGF0YSBhbmFseXNpcy9yZXN1bHRzL2Jvb2xlYW4gcm9idXN0MTUuUm9iaiIpCmBgYAoKCioqU2NhdHRlciBQbG90cyoqCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTl9CmlkcyA8LSBmaXQkcG9zdGVyaW9yc1ssIDE6Ml0KdmFjY2luZVssIDFdIDwtIGFzLmNoYXJhY3Rlcih2YWNjaW5lWywgMV0pCnZhY2NpbmVbLCAxXSA8LSBmYWN0b3IodmFjY2luZVssIDFdLCBsZXZlbHMgPSBsZXZlbHMoaWRzWywgMV0pKQp2YWNjaW5lIDwtIHZhY2NpbmVbIWlzLm5hKHZhY2NpbmVbLCAxXSksIF0KdmFjY2luZSA8LSB2YWNjaW5lW29yZGVyKHZhY2NpbmVbLCAxXSksIF0KaWRzIDwtIG1lcmdlKGlkcywgdmFjY2luZSwgYWxsLnggPSBUUlVFLCBhbGwueSA9IEZBTFNFLAogICAgICAgICAgICAgICAgIGJ5ID0gInB0aWQiLCBzb3J0ID0gRkFMU0UpCnZhY2NpbmF0aW9uIDwtIGlkc1ssIDNdCgpzY2F0dGVyIDwtIHBsb3QoZml0LCB0YXJnZXQgPSB2YWNjaW5hdGlvbiwgdHlwZSA9ICJzY2F0dGVyIiwKICAgICAgICAgICAgICAgIG5jb2wgPSA0KQpzY2F0dGVyICsgdGhlbWVfYncoKQpgYGAKCgoqKlJPQyBwbG90KioKYGBge3IsIHdhcm5pbmc9RkFMU0V9Cgpyb2NwbG90IDwtIHBsb3QoZml0LCB0YXJnZXQgPSB2YWNjaW5hdGlvbiwgdHlwZSA9ICJST0MiLCBuY29scyA9IDUsCiAgICAgZGlyZWN0aW9uID0gImF1dG8iLCB0aHJlc2hvbGRQYWxldHRlID0gTlVMTCwKICAgICBzdWJzZXRzID0gTlVMTCkKcm9jcGxvdApgYGAKCioqRkRSIEN1cnZlcyoqCmBgYHtyLHdhcm5pbmc9RkFMU0V9CnBsb3QoZml0LCB0YXJnZXQgPSB2YWNjaW5hdGlvbiwgdHlwZSA9ICJGRFIiKQpgYGAKCioqUk9DIHRhYmxlKioKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpyb2NSZXN1bHRzIDwtIHJvY1RhYmxlKGZpdCwgdmFjY2luYXRpb24sIGRpcmVjdGlvbiA9ICI+IiwgYWRqdXN0ID0gIkJIIiwKICAgICAgICAgICAgICAgICAgICAgICBzb3J0QVVDID0gRkFMU0UpCnJvY1Jlc3VsdHNbb3JkZXIocm9jUmVzdWx0cyRhdWMsIGRlY3JlYXNpbmcgPSBUUlVFKSwgXQpgYGAKCioqU29tZSBGdW5jdGlvbmFsaXR5IEFuYWx5c2lzKioKYGBge3Isd2FybmluZz1GQUxTRX0KIyBHZXR0aW5nIGluZmVjdGlvbiBzdGF0dXMKaW5mZWN0RGF0IDwtIGRhdGEuZnJhbWUocHRpZCA9IHJ2MTQ0X2NvcnJlbGF0ZXNfZGF0YSRQVElELCBpbmZlY3QgPSBydjE0NF9jb3JyZWxhdGVzX2RhdGEkaW5mZWN0LnkpCmRhdElkIDwtIGFzLmNoYXJhY3RlcihmaXQkcG9zdGVyaW9ycyRwdGlkKQppbmZlY3RJRCA8LSBhcy5jaGFyYWN0ZXIoaW5mZWN0RGF0JHB0aWQpCmluZmVjdERhdCA8LSBpbmZlY3REYXRbaW5mZWN0SUQgJWluJSBkYXRJZCwgXQppbmZlY3REYXQkcHRpZCA8LSBmYWN0b3IoYXMuY2hhcmFjdGVyKGluZmVjdERhdCRwdGlkKSwgbGV2ZWxzID0gbGV2ZWxzKGJvb2xkYXRhJHB0aWQpKQppbmZlY3REYXQgPC0gaW5mZWN0RGF0W29yZGVyKGluZmVjdERhdCRwdGlkKSwgXQppZHMgPC0gbWVyZ2UoaWRzLCBpbmZlY3REYXQsIHNvcnQgPSBGQUxTRSkKaW5mZWN0IDwtIGlkc1ssIDRdCmluZmVjdFtpbmZlY3QgPT0gIlBMQUNFQk8iXSA8LSBOQQoKIyBDb21wdXRpbmcgRnVuY3Rpb25hbGl0eSBTY29yZQpmdW5jIDwtIHJvd1N1bXMoZml0JHBvc3RlcmlvcnNbLCAtMV0pCmZ1bmNBVUMgPC0gcm9jKGluZmVjdCB+IGZ1bmMpJGF1YwpuMCA8LSBzdW0oaW5mZWN0ID09ICJJTkZFQ1RFRCIsIG5hLnJtID0gVFJVRSkKbjEgPC0gc3VtKGluZmVjdCA9PSAiTk9OLUlORkVDVEVEIiwgbmEucm0gPSBUUlVFKQpwcmludCgiQVVDIGZvciBkZXRlY3RpbmcgaW5mZWN0aW9uIGJhc2VkIG9uIGZ1bmN0aW9uYWxpdHkgc2NvcmUuIikKZnVuY0FVQwpwd2lsY294KGZ1bmNBVUMgKiBuMCAqIG4xLCBuMCwgbjEsIGxvd2VyLnRhaWwgPSBGQUxTRSkKCiMgQ29tcHV0aW5nIHBvbHlmdW5jdGlvbmFsaXR5IHNjb3JlCm5mdW5jdGlvbnMgPC0gc2FwcGx5KHN1YnNldHMsIGZ1bmN0aW9uKHgpIGxlbmd0aChncmVnZXhwcigiLCIsIHBhc3RlKCIsIiwgeCkpW1sxXV0pKQpNIDwtIDYKd2VpZ2h0cyA8LSBuZnVuY3Rpb25zIC8gKGNob29zZShNLCBuZnVuY3Rpb25zKSkKcG9seSA8LSBhcHBseShmaXQkcG9zdGVyaW9yc1ssIC0xXSwgMSwgZnVuY3Rpb24oeCkgd2VpZ2h0ZWQubWVhbih4LCB3ZWlnaHRzKSkKIyBwb2x5IDwtIGFwcGx5KGZpdCRwb3N0ZXJpb3JzWywgLTFdLCAxLCBmdW5jdGlvbih4KSBtZWRpYW4od2VpZ2h0cyAqIHgpKQpwb2x5QVVDIDwtIHJvYyhpbmZlY3QgfiBwb2x5KSRhdWMKbjAgPC0gc3VtKGluZmVjdCA9PSAiSU5GRUNURUQiLCBuYS5ybSA9IFRSVUUpCm4xIDwtIHN1bShpbmZlY3QgPT0gIk5PTi1JTkZFQ1RFRCIsIG5hLnJtID0gVFJVRSkKcHJpbnQoIkFVQyBmb3IgZGV0ZWN0aW5nIGluZmVjdGlvbiBiYXNlZCBvbiBwb2x5ZnVuY3Rpb25hbGl0eSBzY29yZS4iKQpwb2x5QVVDCnB3aWxjb3gocG9seUFVQyAqIG4wICogbjEsIG4wLCBuMSwgbG93ZXIudGFpbCA9IEZBTFNFKQoKcHJpbnQoIkFVQ3MgZm9yIGRpY3JpbWluYXRpbmcgYmV0d2VlbiBWYWNjaW5lZXMgYW5kIFBsYWNlYm9zIGJhc2VkIG9uIGZ1bmN0aW9uYWxpdHkgYW5kIHBvbHlmdW5jdGlvbmFsaXR5IHNjb3Jlcy4iKQpyb2ModmFjY2luYXRpb24gfiBmdW5jKQpyb2ModmFjY2luYXRpb24gfiBwb2x5KQpgYGAKCioqUG9seWZ1bmN0aW9uYWxpdHkgQm94cGxvdHMqKgpgYGB7ciwgd2FybmluZz1GQUxTRX0KaGl2IDwtIGluZmVjdApuZnVuY3Rpb25zIDwtIHNhcHBseShuYW1lcyhmaXQkcG9zdGVyaW9ycylbLTFdLCBmdW5jdGlvbih4KSBsZW5ndGgoc3Ryc3BsaXQoeCwgIiwiLCBmaXhlZCA9IFRSVUUpW1sxXV0pKQp3ZWlnaHRzIDwtIGxpc3QoKQp3ZWlnaHRzJEZTIDwtIHJlcCgxLCBuY29sKGZpdCRwb3N0ZXJpb3JzKSAtIDEpCk0gPC0gNgp3ZWlnaHRzJFBGUyA8LSBuZnVuY3Rpb25zIC8gY2hvb3NlKDYsIG5mdW5jdGlvbnMpCmhpdltpcy5uYShoaXYpXSA8LSAiUExBQ0VCTyIKcGxvdChmaXQsIHRhcmdldCA9IGhpdiwgdHlwZSA9ICJib3hwbG90IiwgZ3JvdXBzID0gImFsbCIsIHdlaWdodHMgPSB3ZWlnaHRzKQpgYGAKCgoqKkxvZ2lzdGljIFJlZ3Jlc3Npb25zKioKYGBge3IsIHdhcm5pbmcgPSBGQUxTRX0KZ3JvdXAgPC0gYygyNCwgMjEsIDE1LCA4KQpzY29yZSA8LSByb3dNZWFucyhmaXQkcG9zdGVyaW9yc1ssIGdyb3VwXSkKcm9jZml0IDwtIHJvYyhpbmZlY3QgfiBzY29yZSkKcHdpbGNveChyb2NmaXQkYXVjICogbjAgKiBuMSwgbjAsIG4xLCBsb3dlci50YWlsID0gRkFMU0UpCmlkcyRncm91cHNjb3JlIDwtIHNjb3JlCmlkcyRwb2x5IDwtIHBvbHkKaWRzJGZ1bmMgPC0gZnVuYwoKdmFjY2luZXMgPC0gc3Vic2V0KGNvcnJlbGF0ZXMsIGluZmVjdC55ICE9ICJQTEFDRUJPIikKdmFjY2luZXMkUFRJRCA8LSBhcy5jaGFyYWN0ZXIodmFjY2luZXMkUFRJRCkKaWRzJHB0aWQgPC0gYXMuY2hhcmFjdGVyKGlkcyRwdGlkKQp2YWNjaW5lcyA8LSBtZXJnZSh2YWNjaW5lcywgaWRzLCBhbGwueCA9IFRSVUUsIGFsbC55ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgYnkueCA9ICJQVElEIiwgYnkueSA9ICJwdGlkIikKCnZhY2NpbmVzIDwtIHN1YnNldCh2YWNjaW5lcywgdmFjY2luZXMkaW5mZWN0ICE9ICJQTEFDRUJPIikKCnBsb3QodmFjY2luZXMkUEZTLCB2YWNjaW5lcyRwb2x5LCBtYWluID0gIkNPTVBBU1MgcG9seWZ1bmN0aW9uYWxpdHkgdnMuIG5ldyBwb2x5ZnVuY3Rpb25hbGl0eSIpCmxpbmVzKGxvd2Vzcyh2YWNjaW5lcyRQRlMsIHZhY2NpbmVzJHBvbHkpLCBjb2wgPSAicmVkIiwgbHdkID0gMikKYWJsaW5lKHYgPSBjKDAuMDUsIC4wODUpKQphYmxpbmUoaCA9IGMoMC4zNSkpCnRhcmdldCA8LSB2YWNjaW5lcyRwb2x5ID4gMC4zNSAmIHZhY2NpbmVzJFBGUyA+IDAuMDUgJiB2YWNjaW5lcyRQRlMgPCAwLjA4NQoKc3VtbWFyeShnbG0oaW5mZWN0IH4gZnVuYyArIElnQXByaW0gKyByaXNrLm1lZGl1bSArIHJpc2suaGlnaCArIHNleCwKICAgICAgICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgICAgICAgZGF0YSA9IHZhY2NpbmVzKSkKCnN1bW1hcnkoZ2xtKGluZmVjdCB+IHBvbHkgKyBJZ0FwcmltICsgcmlzay5tZWRpdW0gKyByaXNrLmhpZ2ggKyBzZXgsCiAgICAgICAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgICAgICAgIGRhdGEgPSB2YWNjaW5lcykpCgpzdW1tYXJ5KGdsbShpbmZlY3QgfiBncm91cHNjb3JlICsgSWdBcHJpbSArIHJpc2subWVkaXVtICsgcmlzay5oaWdoICsgc2V4LAogICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICAgICAgICBkYXRhID0gdmFjY2luZXMpKQoKYGBgCgoqKkxvZ2lzdGljIFJlZ3Jlc3Npb25zIGZvciBTaW5nbGUgU3Vic2V0cyoqCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQp2YWNjaW5lcyA8LSBzdWJzZXQoY29ycmVsYXRlcywgaW5mZWN0LnkgIT0gIlBMQUNFQk8iKQpyZXN1bHRMaXN0IDwtIGxpc3QoKQphZGpSb2NMaXN0IDwtIGxpc3QoKQpmb3IoaSBpbiAxOihuY29sKGZpdCRwb3N0ZXJpb3JzKSAtIDEpKSB7CiAgdmFjY2luZXMkcG9zdCA8LSBOVUxMCiAgcG9zdCA8LSBmaXQkcG9zdGVyaW9yc1shaXMubmEoaW5mZWN0KSwgYygxLCBpICsgMSldCiAgbmFtZXMocG9zdClbMl0gPC0gInBvc3QiCiAgdmFjY2luZXMgPC0gbWVyZ2UodmFjY2luZXMsIHBvc3QsIGJ5LnggPSAiUFRJRCIsIGJ5LnkgPSAicHRpZCIsIGFsbC54ID0gVFJVRSkKICByZXN1bHRMaXN0W1tpXV0gPC0gc3VtbWFyeShnbG0oaW5mZWN0LnkgfiBwb3N0ICsgSWdBcHJpbSArIGFnZWNhdCArIHJpc2subWVkaXVtICsgcmlzay5oaWdoICsgc2V4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdmFjY2luZXMpKQogIHJlc2lkIDwtIGxtKHBvc3QgfiBJZ0FwcmltICsgYWdlY2F0ICsgcmlzay5tZWRpdW0gKyByaXNrLmhpZ2ggKyBzZXgsCiAgICAgICAgICAgICAgZGF0YSA9IHZhY2NpbmVzKSRyZXNpZHVhbHMKICBpbmZlY3RSZXNpZCA8LSBnbG0oaW5mZWN0LnkgIH4gSWdBcHJpbSArIGFnZWNhdCArIHJpc2subWVkaXVtICsgcmlzay5oaWdoICsgc2V4LAogICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiYmlub21pYWwiLCBkYXRhID0gdmFjY2luZXMpJHJlc2lkdWFscwoKICBhZGpSb2NMaXN0W1tpXV0gPC0gcm9jKHZhY2NpbmVzJGluZmVjdC55IH4gcmVzaWQpCn0KCm5hbWVzKHJlc3VsdExpc3QpIDwtIGNvbG5hbWVzKGZpdCRwb3N0ZXJpb3JzKVstMV0KbmFtZXMoYWRqUm9jTGlzdCkgPC0gY29sbmFtZXMoZml0JHBvc3RlcmlvcnMpWy0xXQpyZWdSZXN1bHQgPC0gdChzYXBwbHkocmVzdWx0TGlzdCwgZnVuY3Rpb24oeCkgeCRjb2VmZmljaWVudFsyLCBjKDEsNCldKSkKcmVnUmVzdWx0IDwtIGRhdGEuZnJhbWUocmVnUmVzdWx0KQpyZWdSZXN1bHQkYXVjIDwtIHNhcHBseShhZGpSb2NMaXN0LCBmdW5jdGlvbih4KSB4JGF1YykKcmVnUmVzdWx0JGF1Y1B2YWwgPC0gcHdpbGNveChyZWdSZXN1bHQkYXVjICogbjAgKiBuMSwgbjAsIG4xLCBsb3dlci50YWlsID0gRkFMU0UpCnJlZ1Jlc3VsdCRhdWNRdmFsIDwtIHAuYWRqdXN0KHJlZ1Jlc3VsdCRhdWNQdmFsLCBtZXRob2QgPSAiQkgiKQpyZWdSZXN1bHRbb3JkZXIocmVnUmVzdWx0WywgMl0sIGRlY3JlYXNpbmcgPSBGQUxTRSksIF0KYGBgCgoqKkRlcGVuZGVuY2UgU3RydWN0dXJlIGZvciBSYW5kb20gRWZmZWN0cyBmb3IgVmFyeWluZyBUaHJlc2hvbGQqKgpgYGB7cix3YXJuaW5nPUZBTFNFfQojIHJhbmRTdGFiaWxpdHkgPC0gc3RhYmlsaXR5R3JhcGgoZml0LCB0eXBlID0gInJhbmRvbUVmZmVjdHMiLCBjcHVzID0gMiwgcmVwcyA9IDEwMCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGN2ID0gVFJVRSkKIyBzYXZlKHJhbmRTdGFiaWxpdHksIGZpbGUgPSAiZGF0YSBhbmFseXNpcy9yZXN1bHRzL1JWMTQ0cmFuZDE1LlJvYmoiKQpsb2FkKCJkYXRhIGFuYWx5c2lzL3Jlc3VsdHMvUlYxNDRyYW5kMTUuUm9iaiIpCmZvcih0aHJlc2hvbGQgaW4gYygwLjUsIDAuNzUsIDAuODUsIDAuOSwgMC45NSwgMSkpIHsKICByYW5kcGxvdCA8LSBwbG90KHJhbmRTdGFiaWxpdHksIGZpbGwgPSByb2NSZXN1bHRzJGF1YywgdGhyZXNob2xkID0gdGhyZXNob2xkKQogIHByaW50KHJhbmRwbG90KQp9CgpgYGAKCgoqKklzaW5nIE5ldHdvcmsgZm9yIFZhcnlpbmcgVGhyZXNob2xkKioKYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0KIyBzdGFiaWxpdHkgPC0gc3RhYmlsaXR5R3JhcGgoZml0LCB0eXBlID0gImlzaW5nIiwgY3B1cyA9IDIsIHJlcHMgPSAxMDApCiMgc2F2ZShzdGFiaWxpdHksIGZpbGUgPSAiZGF0YSBhbmFseXNpcy9yZXN1bHRzL1JWMTQ0aXNpbmcxNS5Sb2JqIikKbG9hZCgiZGF0YSBhbmFseXNpcy9yZXN1bHRzL1JWMTQ0aXNpbmcxNS5Sb2JqIikKZm9yKHRocmVzaG9sZCBpbiBjKDAuNSwgMC43NSwgMC44NSwgMC45LCAwLjk1LCAxKSkgewogIGlzaW5ncGxvdCA8LSBwbG90KHN0YWJpbGl0eSwgZmlsbCA9IHJvY1Jlc3VsdHMkYXVjLCB0aHJlc2hvbGQgPSB0aHJlc2hvbGQpCiAgcHJpbnQoaXNpbmdwbG90KQp9CgpgYGAKCg==